/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2021 Huawei Technologies Co., Ltd */

#ifndef HINIC3_TX_H
#define HINIC3_TX_H

#include <net/ipv6.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
#include <linux/ip.h>
#include <linux/ipv6.h>

#include "hinic3_nic_qp.h"
#include "hinic3_nic_io.h"

/**
 * VXLAN_OFFLOAD_PORT_LE - VXLAN 默认端口的小端表示
 * 说明：硬件期望以小端写入端口，big-end 对应 4789。
 */
#define VXLAN_OFFLOAD_PORT_LE 46354 /* big end is 4789 */

/**
 * COMPACET_WQ_SKB_MAX_LEN - 紧凑型 WQE 模式下可处理的 SKB 最大长度
 */
#define COMPACET_WQ_SKB_MAX_LEN 16383

/*
 * L3/L4 头部字段帮助宏：用于计算版本与头部长度单位。
 */
#define IP4_VERSION 4
#define IP6_VERSION 6
#define IP_HDR_IHL_UNIT_SHIFT 2
#define TCP_HDR_DATA_OFF_UNIT_SHIFT 2

/**
 * enum tx_offload_type - 发送卸载类型位图
 * @TX_OFFLOAD_TSO: TCP 分片卸载
 * @TX_OFFLOAD_CSUM: L4 校验和卸载
 * @TX_OFFLOAD_VLAN: VLAN 插入卸载
 * @TX_OFFLOAD_INVALID: 非法/保留位
 * @TX_OFFLOAD_ESP: IPsec ESP 相关卸载
 */
enum tx_offload_type {
	TX_OFFLOAD_TSO = BIT(0),
	TX_OFFLOAD_CSUM = BIT(1),
	TX_OFFLOAD_VLAN = BIT(2),
	TX_OFFLOAD_INVALID = BIT(3),
	TX_OFFLOAD_ESP = BIT(4),
};

/**
 * struct hinic3_txq_stats - TX 队列统计
 * @packets: 成功发送的包数
 * @bytes: 成功发送的字节数
 * @busy: 队列 busy 次数
 * @wake: 唤醒队列次数
 * @dropped: 丢包数（开始发送前被丢弃）
 * @skb_pad_err: SKB pad 错误
 * @frag_len_overflow: 分片长度溢出
 * @offload_cow_skb_err: 复制写时(offload)组包失败
 * @map_frag_err: DMA 映射分片失败
 * @unknown_tunnel_pkt: 未知隧道类型数据包
 * @frag_size_err: 分片尺寸错误
 * @syncp: u64 统计同步原语
 */
struct hinic3_txq_stats {
	u64	packets;
	u64	bytes;
	u64	busy;
	u64	wake;
	u64	dropped;

	/* Subdivision statistics show in private tool */
	u64	skb_pad_err;
	u64	frag_len_overflow;
	u64	offload_cow_skb_err;
	u64	map_frag_err;
	u64	unknown_tunnel_pkt;
	u64	frag_size_err;
	u64	rsvd1;
	u64	rsvd2;

#ifdef HAVE_NDO_GET_STATS64
	struct u64_stats_sync	syncp;
#else
	struct u64_stats_sync_empty syncp;
#endif
};

/**
 * struct hinic3_dma_info - DMA 地址与长度描述
 * @dma: DMA 物理地址
 * @len: 映射长度
 */
struct hinic3_dma_info {
	dma_addr_t dma;
	u32 len;
};

/* IP 版本/偏移帮助宏（发送路径内部使用） */
#define IPV4_VERSION		4
#define IPV6_VERSION		6
#define TCP_HDR_DOFF_UNIT	2
#define TRANSPORT_OFFSET(l4_hdr, skb) ((u32)((l4_hdr) - (skb)->data))

/**
 * union hinic3_ip - IPv4/IPv6 头指针共用体
 * @v4: IPv4 头
 * @v6: IPv6 头
 * @hdr: 作为字节指针访问
 */
union hinic3_ip {
	struct iphdr *v4;
	struct ipv6hdr *v6;
	unsigned char *hdr;
};

/**
 * struct hinic3_tx_info - TX WQE 级上下文
 * @skb: 对应的待发送 SKB
 * @wqebb_cnt: 占用的 WQEBB 数量
 * @valid_nr_frags: 有效分片数
 * @num_sge: 使用的 SGE 数
 * @num_pkts: 合并发送的包数（UFO/TSO 等场景）
 * @num_bytes: 发送的累计字节数
 * @dma_info: DMA 映射数组
 */
struct hinic3_tx_info {
	struct sk_buff		*skb;

	u16			wqebb_cnt;
	u16			valid_nr_frags;

	int			num_sge;
	u16			num_pkts;
	u16			rsvd1;
	u32			rsvd2;
	u64			num_bytes;
	struct hinic3_dma_info	*dma_info;
	u64			rsvd3;
};

/**
 * struct hinic3_txq - TX 队列上下文
 * @netdev: 绑定的 net_device
 * @dev: Linux 设备对象（DMA 映射）
 * @txq_stats: 队列统计
 * @cos: Class of Service
 * @q_id: 队列索引
 * @q_mask/q_depth: 队列掩码与深度
 * @tx_info: WQE 级上下文数组
 * @sq: 发送队列（IO 队列）
 * @last_moder_packets/bytes: 用于自适应中断合并的历史统计
 */
struct hinic3_txq {
	struct net_device	*netdev;
	struct device		*dev;

	struct hinic3_txq_stats txq_stats;

	u8			cos;
	u8			rsvd1;
	u16			q_id;
	u32			q_mask;
	u32			q_depth;
	u32			rsvd2;

	struct hinic3_tx_info	*tx_info;
	struct hinic3_io_queue	*sq;

	u64			last_moder_packets;
	u64			last_moder_bytes;
	u64			rsvd3;
} ____cacheline_aligned;

/**
 * hinic3_lb_xmit_frame - loopback 或内测专用发送路径
 * @skb: 要发送的 SKB
 * @netdev: 目标 net_device
 * Return: NETDEV_TX_OK/NETDEV_TX_BUSY
 */
netdev_tx_t hinic3_lb_xmit_frame(struct sk_buff *skb,
				 struct net_device *netdev);

/**
 * struct hinic3_dyna_txq_res - 动态队列重建时的资源承载
 * @tx_info: TX WQE 上下文数组
 * @bds: 发送 BD 区域的 DMA 描述数组
 */
struct hinic3_dyna_txq_res {
	struct hinic3_tx_info *tx_info;
	struct hinic3_dma_info *bds;
};

/**
 * hinic3_xmit_frame - 标准发送路径入口
 * @skb: SKB 数据包
 * @netdev: 设备
 */
netdev_tx_t hinic3_xmit_frame(struct sk_buff *skb, struct net_device *netdev);

/**
 * hinic3_txq_get_stats - 获取 TXQ 统计（带同步）
 * @txq: TX 队列
 * @stats: 输出统计
 */
void hinic3_txq_get_stats(struct hinic3_txq *txq,
			  struct hinic3_txq_stats *stats);

/**
 * hinic3_txq_clean_stats - 清零 TXQ 统计
 * @txq_stats: 统计对象
 */
void hinic3_txq_clean_stats(struct hinic3_txq_stats *txq_stats);

struct hinic3_nic_dev;
/**
 * hinic3_alloc_txqs_res - 分配 TX 队列资源（重建准备）
 * @nic_dev: 设备
 * @num_sq: 队列数
 * @sq_depth: 队列深度
 * @txqs_res: 输出资源数组
 */
int hinic3_alloc_txqs_res(struct hinic3_nic_dev *nic_dev, u16 num_sq,
			  u32 sq_depth, struct hinic3_dyna_txq_res *txqs_res);

/**
 * hinic3_free_txqs_res - 释放 TX 队列资源
 */
void hinic3_free_txqs_res(struct hinic3_nic_dev *nic_dev, u16 num_sq,
			  u32 sq_depth, struct hinic3_dyna_txq_res *txqs_res);

/**
 * hinic3_configure_txqs - 配置 TX 队列并完成映射
 */
int hinic3_configure_txqs(struct hinic3_nic_dev *nic_dev, u16 num_sq,
			  u32 sq_depth, struct hinic3_dyna_txq_res *txqs_res);

/**
 * hinic3_alloc_txqs - 按 net_device 配置分配 TX 队列
 */
int hinic3_alloc_txqs(struct net_device *netdev);

/**
 * hinic3_free_txqs - 释放 net_device 的 TX 队列
 */
void hinic3_free_txqs(struct net_device *netdev);

/**
 * hinic3_tx_poll - NAPI 轮询中的 TX 完成回收
 * @txq: 队列
 * @budget: 本轮预算
 * Return: 回收包数
 */
int hinic3_tx_poll(struct hinic3_txq *txq, int budget);

/**
 * hinic3_flush_txqs - 刷新并等待 TX 队列完成
 */
int hinic3_flush_txqs(struct net_device *netdev);

/**
 * hinic3_set_txq_cos - 批量设置 TX 队列的 CoS
 */
void hinic3_set_txq_cos(struct hinic3_nic_dev *nic_dev, u16 start_qid,
			u16 q_num, u8 cos);

#ifdef static
#undef static
#define LLT_STATIC_DEF_SAVED
#endif

static inline __sum16 csum_magic(union hinic3_ip *ip, unsigned short proto)
{
	return (ip->v4->version == IPV4_VERSION) ?
		csum_tcpudp_magic(ip->v4->saddr, ip->v4->daddr, 0, proto, 0) :
		csum_ipv6_magic(&ip->v6->saddr, &ip->v6->daddr, 0, proto, 0);
}

#endif
